#ifndef K_LIST_H
#define K_LIST_H
#pragma once

#include "KLockable.h"

class KList : public KLockable {
public:
    class Node : ::KNoCopy {
    public:
        explicit Node(Node *p = NULL, Node *n = NULL);
        virtual ~Node() = 0;
    public:
        Node *next() const      { return m_next; }
        Node *prev() const      { return m_prev; }
    private:
        Node *m_next;
        Node *m_prev;
        friend class KList;
    };
    typedef Node *NodePtr;
public:
    explicit KList(size_t max = 0);
    virtual ~KList()            { destroy(); }
public:
    NodePtr back() const        { return m_back; }
    NodePtr front() const       { return m_front; }
    bool isEmpty() const        { return !size(); }
    size_t maxSize() const      { return m_max; }
    void setMaxSize(size_t max);
    size_t size() const         { return m_size; }
public:
    NodePtr cutOff(NodePtr node);
    void destroy();
    NodePtr popBack();
    NodePtr popFront();
    void pushBack(NodePtr node);
    void pushFront(NodePtr node);
public:
    NodePtr cutOffUnlocked(NodePtr node);
    NodePtr popBackUnlocked()   { return cutOffUnlocked(back()); }
    NodePtr popFrontUnlocked()  { return cutOffUnlocked(front()); }
    void pushBackUnlocked(NodePtr node);
    void pushFrontUnlocked(NodePtr node);
private:
    volatile NodePtr m_back, m_front;
    size_t m_max;
    volatile size_t m_size;
};

inline void KList::setMaxSize(size_t max)
{
    KLocker lock(*this);
    if ((m_max = max)) {
        while (m_size > m_max) {
            delete popFrontUnlocked();
        }
    }
}

inline KList::NodePtr KList::cutOff(NodePtr node)
{
    KLocker lock(*this);
    return cutOffUnlocked(node);
}

inline void KList::destroy()
{
    KLocker lock(*this);
    while (m_size > 0) {
        delete popFrontUnlocked();
    }
}

inline KList::NodePtr KList::popBack()
{
    KLocker lock(*this);
    return popBackUnlocked();
}

inline KList::NodePtr KList::popFront()
{
    KLocker lock(*this);
    return popFrontUnlocked();
}

inline void KList::pushBack(NodePtr node)
{
    KLocker lock(*this);
    return pushBackUnlocked(node);
}

inline void KList::pushFront(NodePtr node)
{
    KLocker lock(*this);
    return pushFrontUnlocked(node);
}

#endif // K_LIST_H